package cn.bluethink.eguan.rewardtask.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;

import cn.bluethink.eguan.core.service.EgAcountService;
import cn.bluethink.eguan.core.service.EgFactionService;
import cn.bluethink.eguan.core.service.EgMemberService;
import cn.bluethink.eguan.core.service.EgPosObjectService;
import cn.bluethink.eguan.core.service.EgTaskService;
import cn.bluethink.eguan.core.service.EgTavernService;
import cn.bluethink.eguan.core.service.EgUserService;
import cn.bluethink.eguan.model.core.EgAcount;
import cn.bluethink.eguan.model.core.EgConfigInfo;
import cn.bluethink.eguan.model.core.EgOType;
import cn.bluethink.eguan.model.core.EgObject;
import cn.bluethink.eguan.model.core.EgPurview;
import cn.bluethink.eguan.model.core.EgTask;
import cn.bluethink.eguan.model.core.EgTaskStatus;
import cn.bluethink.eguan.model.core.EgUser;
import cn.bluethink.eguan.model.rewardtask.EgRewardTask;
import cn.bluethink.eguan.result.PageInfo;
import cn.bluethink.eguan.rewardtask.entity.EgRewardTaskEntity;
import cn.bluethink.eguan.rewardtask.entity.RewardTaskEntity;
import cn.bluethink.eguan.rewardtask.filter.RewardTaskFilter;
import cn.bluethink.eguan.rewardtask.mapper.EgRewardTaskMapper;
import cn.bluethink.eguan.utils.DateUtil;
import tk.mybatis.mapper.entity.Example;

/**
 * 悬赏任务业务层
 * 
 * @author 苏州中科蓝迪公司所有(c)2016-2021
 * @version 0.2.0
 * @brief 驿馆+产品
 * @note 修订历史： 1.wanglei于2018/10/18设计并构建初始版本v0.2.0 2.liuxn于2018/11/2修订修改逻辑
 *       3.wanglei于2018/11/8优化过滤查询方式为拼接SQL 4.wanglei于2018/11/15修订悬赏任务打赏时的账户处理
 */
@Service
public class EgRewardTaskService {

    @Autowired
    private EgRewardTaskMapper rewardTaskMapper;

    @Autowired
    private EgTaskService taskService;

    @Autowired
    private EgAcountService acountService;

    @Autowired
    private EgReceiverService receiverService;

    @Autowired
    private EgTavernService tavernService;

    @Autowired
    private EgPosObjectService posObjService;

    @Autowired
    private EgFactionService factionService;

    @Autowired
    private EgMemberService memberService;
    
    @Autowired
    private EgUserService userService;

    @Autowired
    private EgConfigInfo configInfo;

    /**
     * 发布个人或团队悬赏任务 。
     * 
     * @param rewardTask
     * @param user
     * @param acountId
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgRewardTask create(EgRewardTask rewardTask, EgUser user, Long acountId) throws Exception {
        // 获取悬赏的金币
        Integer btCcoin = rewardTask.getBtCoin();
        if (btCcoin == null || btCcoin <= 0) {
            throw new RuntimeException("悬赏金币不能为空并且大于0！");
        }
        // 账户校验
        if (!verifyAcount(btCcoin, acountId)) {
            throw new RuntimeException("账户不存在或余额不足，创建失败！");
        }
        // 设置悬赏任务类型;发布者基本信息;发布位置;
        rewardTask.setType(new EgOType(EgOType.REWARD_TASK, null));
        rewardTask.setCuser(new EgUser(user.getId(), user.getName(), user.getAvatar()));
        rewardTask.setPos(user.getPos());
        // 设置实际截止时间为截止日期当天晚上12点
        if (rewardTask.getEtime() != null) {
            rewardTask.setEtime(DateUtil.dateTime(rewardTask.getEtime(), 23, 59, 59));
        }
        // 创建通用任务,设置悬赏任务ID
        EgTask task = taskService.create(rewardTask);
        if (task == null) {
            return null;
        }
        rewardTask.setId(task.getId());
        EgRewardTaskEntity entity = new EgRewardTaskEntity(rewardTask, acountId);
        if (rewardTaskMapper.insertSelective(entity) == 0) {
            return null;
        }
        return entity.createEgRewardTask(task);
    }

    /**
     * 根据主键Id删除悬赏任务。(修改为逻辑删除)
     * 
     * @param tid
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public Boolean del(Long tid) throws Exception {
        // 删除领取者记录
        int selectCount = receiverService.getReceiversCount(tid, null);
        if (selectCount > 0) {
            int delCount = receiverService.delReceiversById(tid);
            if (selectCount != delCount) {
                throw new RuntimeException("删除失败，请重试！");
            }
        }
        // 根据主键ID删除悬赏任务
        if (rewardTaskMapper.deleteByPrimaryKey(tid) == 0) {
            throw new RuntimeException("删除失败，请重试！");
        }
        // 根据主键ID和任务类型逻辑删除通用任务
        if (taskService.remove(tid, EgOType.REWARD_TASK) == 0) {
            throw new RuntimeException("删除失败，请重试！");
        }
        return true;
    }

    /**
     * 更新悬赏任务的基本属性(不包括状态)。 必须在任务未开始前编辑基本信息。
     * 
     * @param oldTask
     * @param upTask
     * @param user
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgRewardTask updateProperty(EgRewardTask oldTask, EgRewardTask upTask, EgUser user) throws Exception {
        // 更新任务名称
        String name = upTask.getName();
        if (StringUtils.isNotBlank(name) && !name.equals(oldTask.getName())) {
            oldTask.setName(name);
        }
        // 更新任务内容
        String content = upTask.getContent();
        if (StringUtils.isNotBlank(content) && !content.equals(oldTask.getContent())) {
            oldTask.setContent(content);
        }
        // 待更新任务截至时间
        Date upEtime = upTask.getEtime();
        if (upEtime != null && DateUtil.dateCompare(upEtime, oldTask.getEtime()) != 0) {
            oldTask.setEtime(DateUtil.dateTime(upEtime, 23, 59, 59));
        }
        // 更新悬赏BT
        Integer btCoin = upTask.getBtCoin();
        if (btCoin != null && !btCoin.equals(oldTask.getBtCoin())) {
            EgRewardTaskEntity entity = rewardTaskMapper.selectByPrimaryKey(oldTask.getId());
            if (entity == null) {
                return null;
            }
            // 账户校验
            if (!verifyAcount(btCoin, entity.getAcount())) {
                throw new RuntimeException("账户不存在或余额不足，编辑失败！");
            }
            oldTask.setBtCoin(btCoin);
        }
        // 更新限制人数
        Integer upLimit = upTask.getLimit();
        if (upLimit != null && !upLimit.equals(oldTask.getLimit())) {
            // 获取悬赏任务的特有属性
            EgRewardTask reward = queryById(upTask.getId());
            if (reward == null) {
                return null;
            }
            // 获取已领取的人数
            Integer count = reward.getRvcount();
            // 【无限制】 或者 【有限制并且限制人数大于已被指派人数时】
            if (upLimit == 0 || (upLimit > 0 && upLimit > count)) {
                oldTask.setLimit(upLimit);
            }
        }
        return oldTask;
    }

    /**
     * 更新悬赏任务(基本信息，任务状态)。
     * 
     * @param rewardtask
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgRewardTask update(EgRewardTask rewardTask, Long acountId) throws Exception {
        // 创建实体
        EgRewardTaskEntity entity = new EgRewardTaskEntity(rewardTask, acountId);
        // 更新通用任务(不包括状态)
        EgTask task = taskService.update(rewardTask);
        if (task == null) {
            throw new RuntimeException("更新失败！");
        }
        // 更新悬赏任务
        int updateCount = rewardTaskMapper.updateByPrimaryKeySelective(entity);
        return updateCount > 0 ? entity.createEgRewardTask(task) : null;
    }

    /**
     * 根据主键Id获取悬赏任务特有属性。(此方法单表操作，主要获取更新时间的时间戳)
     * 
     * @param tid
     * @return
     * @throws Exception
     */
    public EgRewardTask queryById(Long tid) throws Exception {
        // 查询悬赏任务
        EgRewardTaskEntity entity = rewardTaskMapper.selectByPrimaryKey(tid);
        if (entity == null) {
            return null;
        }
        EgRewardTask rewardTask = entity.createEgRewardTask();
        return rewardTask != null ? rewardTask : null;
    }

    /**
     * 根据悬赏任务的ID列表查询
     * 
     * @param tids
     * @return
     * @throws Exception
     */
    public List<EgRewardTask> queryByIds(List<Long> tids) throws Exception {
        // 返回结果
        List<EgRewardTask> result = new ArrayList<>();
        // 根据悬赏任务的ID列表查询
        List<RewardTaskEntity> entities = rewardTaskMapper.queryByIds(tids);
        if (entities != null && entities.size() > 0) {
            for (RewardTaskEntity entity : entities) {
                EgRewardTask rewardTask = entity.createRewardTask();
                // 获取位置的信息
                EgObject pos = posObjService.pos(entity.getPosOtype(), entity.getPosOid());
                if (pos != null) {
                    rewardTask.setPos(pos);
                } else {
                    rewardTask.setPos(
                            new EgObject(0L, configInfo.jianghu().getName(), new EgOType(EgOType.JIANGHU, null)));
                }
                result.add(rewardTask);
            }
            return result;
        }
        return null;
    }

    /**
     * 只更新悬赏的特有属性字段。(此方法单表操作，利用更新时间的时间戳进行乐观锁操作)
     * 
     * @param rewardTask
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgRewardTask updateRewardTask(EgRewardTask rewardTask) throws Exception {
        EgRewardTaskEntity entity = new EgRewardTaskEntity(rewardTask, null);
        // 构造条件，注意【更新时间】条件的限制
        Example example = new Example(EgRewardTaskEntity.class);
        example.and().andEqualTo("tid", rewardTask.getId()).andEqualTo("utime", rewardTask.getUtime());
        // 更新悬赏任务特有属性字段
        int updateCount = rewardTaskMapper.updateByExampleSelective(entity, example);
        return updateCount > 0 ? entity.createEgRewardTask() : null;
    }

    /**
     * 根据主键Id获取悬赏任务详情
     * 
     * @param tid
     * @return
     * @throws Exception
     */
    public EgRewardTask detail(Long tid) throws Exception {
        // 查询通用任务详情
        EgTask task = taskService.detail(tid, EgOType.REWARD_TASK);
        if (task == null) {
            return null;
        }
        // 查询悬赏任务
        EgRewardTaskEntity entity = rewardTaskMapper.selectByPrimaryKey(tid);
        if (entity == null) {
            return null;
        }
        return entity.createEgRewardTask(task);
    }

    /**
     * 根据悬赏任务ID,用户ID获取相应的账户ID
     * 
     * @param tid
     * @param uid
     * @return
     * @throws Exception
     */
    public Map<String, Long> getAcountId(Long tid, Long uid) throws Exception {
        Map<String, Long> acountMap = new HashMap<>();
        // 获取悬赏任务实体信息
        EgRewardTaskEntity entity = rewardTaskMapper.selectByPrimaryKey(tid);
        if (entity != null) {
            acountMap.put("taskAcount", entity.getAcount());
        }
        // 获取该揭榜者的基本信息
        EgUser user = userService.getUser(uid, false);
        if (user != null) {
            acountMap.put("userAcount", user.getAcount().getId());
        }
        return acountMap;
    }
    
    /**
     * 根据悬赏任务的类型（个人、团队）获取引用的账户ID
     * 
     * @param oid
     *            任务的发布位置ID
     * @param otype
     *            发布位置类型ID
     * @param bmine
     *            是否为个人悬赏：true表示为个人
     * @param user
     * @return
     * @throws Exception
     */
    public Long getAcountByTaskType(Long oid, Integer otype, Boolean bmine, EgUser user) throws Exception {
        Long acount = 0L;
        // 如果是个人悬赏任务
        if (bmine) {
            acount = user.getAcount().getId();
        } else {
            // 判断该用户是否具有团队悬赏的权限，有则将个人改成团队(将个人账户改为团队账户)
            if (otype.equals(EgOType.FACTION)) {
                // 获取门派成员信息,判断该用户在该门派中是否具有团队悬赏权限
                EgUser member = memberService.queryById(oid, user.getId());
                if (member == null) {
                    throw new RuntimeException("非本派成员无权操作！");
                }
                if ((member.getRole().getPurview() & EgPurview.TEAM_REWARD_TASK) == 0) {
                    throw new RuntimeException("用户权限不足，禁止访问！");
                }
                // 获取门派账户信息,创建团队悬赏任务，存入门派账户ID
                Long fAcount = factionService.getAcountByFid(oid);
                if (fAcount == 0L) {
                    throw new RuntimeException("门派账户不存在！");
                }
                acount = fAcount;
            } else if (otype.equals(EgOType.JIANGHU)) {
                // 判断该用户在该江湖中是否具有团队悬赏权限，有则创建团队悬赏任务，存入江湖账户ID
                if ((user.getRole().getPurview() & EgPurview.TEAM_REWARD_TASK) == 0) {
                    throw new RuntimeException("用户权限不足，禁止访问！");
                }
                acount = configInfo.getAcountId();
            } else {
                // 在驿馆则没有修改类型的操作
                throw new RuntimeException("驿馆内无此项操作！");
            }
        }
        return acount;
    }

    /**
     * 根据过滤条件查询悬赏任务列表。
     * 1.根据发布者Id查询发布的悬赏任务列表，默认待打赏的(完成的)在最前面，其余按照创建时间降序排序；根据任务状态筛选后，按照创建时间降序排序；
     * 2.根据任务的发布位置对象ID查询，只查询进行中的，并且没有过期的任务，按照创建时间降序排序。
     * 
     * @param filter
     * @return
     * @throws Exception
     */
    public PageInfo<EgRewardTask> query(RewardTaskFilter filter) throws Exception {
        Integer pageNum = filter.getPageNum() == null ? 1 : filter.getPageNum();
        Integer pageSize = filter.getPageSize() == null ? 50 : filter.getPageSize();
        // 构造查询条件
        String whereSql = buildQuerySql(filter);
        // 返回结果
        List<EgRewardTask> result = new ArrayList<>();
        List<RewardTaskEntity> entities = new ArrayList<>();
        // 判断whereSql不为空，根据条件查询
        if (StringUtils.isNotBlank(whereSql)) {
            // 分页
            PageHelper.startPage(pageNum, pageSize);
            entities = rewardTaskMapper.selectTasksByCondition(whereSql);
        }
        PageInfo<RewardTaskEntity> oldPageInfo = new PageInfo<>(entities);
        if (entities != null && entities.size() > 0) {
            for (RewardTaskEntity entity : entities) {
                EgRewardTask rewardTask = entity.createRewardTask();
                // 获取位置的信息
                EgObject pos = posObjService.pos(entity.getPosOtype(), entity.getPosOid());
                if (pos != null) {
                    rewardTask.setPos(pos);
                } else {
                    rewardTask.setPos(
                            new EgObject(0L, configInfo.jianghu().getName(), new EgOType(EgOType.JIANGHU, null)));
                }
                result.add(rewardTask);
            }
        }
        return new PageInfo<>(result, oldPageInfo);
    }

    /**
     * 定时清除通用任务删除状态为1的赏金任务
     * 
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    @Scheduled(cron = "${timedDel.date}")
    public void delRewardTask() throws Exception {
        // 删除赏金任务
        rewardTaskMapper.del();
        // 删除任务接收者
        rewardTaskMapper.delReceivers();
    }

    /**
     * 构建query查询条件
     * 
     * @param filter
     * @return
     * @throws Exception
     */
    private String buildQuerySql(RewardTaskFilter filter) throws Exception {
        String whereSql = "";
        List<String> whereSqlList = new ArrayList<>();
        /*
         * 声明过滤条件(包括：对象ID/对象ID是否为创建者ID的逻辑标识/任务的状态)
         */
        Long id = filter.getId() == null ? 0 : filter.getId();
        Boolean buid = filter.getBuid() == null ? false : filter.getBuid();
        Integer status = filter.getStatus();
        // 获取门派ID
        Long fid = filter.getFid() == null ? 0 : filter.getFid();

        whereSqlList.add(" 1 = 1");
        // 任务删除状态为0（未删除）
        whereSqlList.add(" T.del_flag = 0 ");
        // 任务类型为悬赏任务
        whereSqlList.add(" T.otype = " + EgOType.REWARD_TASK);
        // 对象ID不为0且为创建者ID
        if (id > 0 && buid) {
            whereSqlList.add(" T.cuid = " + id);
            // 默认查询所有的；如果门派ID不为空，查询该门派(以及门派下所有驿馆)下发布的悬赏任务;
            if (fid != 0) {
                List<Long> tavernIds = tavernService.egidsByFid(fid);
                if (tavernIds != null && tavernIds.size() > 0) {
                    whereSqlList.add(
                            " (T.pos_oid = " + fid + " OR T.pos_oid IN (" + StringUtils.join(tavernIds, ",") + "))");
                }
            }
        }
        // 对象ID为位置对象ID(若ID为0表示为江湖),未到截至时间，状态进行中
        if (!buid) {
            whereSqlList.add(" T.pos_oid = " + id);
            whereSqlList.add(" T.etime >= NOW() ");
            whereSqlList.add(" T.status= " + EgTaskStatus.STATUS_HANDLE);
        }
        // 按照状态排序
        if (status != null) {
            whereSqlList.add(" T.status= " + status);
        }
        // 设置查询条件AND连接符
        if (whereSqlList.size() > 0) {
            whereSql += String.join(" AND ", whereSqlList);
        }
        // 按照创建时间降序排序
        whereSql += " ORDER BY T.utime Desc ";
        return whereSql;
    }

    /**
     * 校验账户是否有足够的悬赏金币
     * 
     * @param btCoin
     * @param acountId
     * @return
     * @throws Exception
     */
    private Boolean verifyAcount(Integer btCoin, Long acountId) throws Exception {
        EgAcount acount = acountService.query(acountId);
        if (acount == null) {
            return false;
        }
        return acount.getBt() < btCoin ? false : true;
    }
}
